package org.glassunit.core.util;

import com.sun.enterprise.deploy.shared.FileArchive;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.DefaultArtifactRepository;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.embedder.MavenEmbedder;
import org.apache.maven.embedder.MavenEmbedderException;
import org.apache.maven.model.Dependency;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingException;
import org.glassfish.deployment.common.GenericAnnotationDetector;
import org.glassfish.ejb.EjbSniffer;
import org.glassfish.web.sniffer.WebSniffer;
import org.glassunit.core.deploy.ApplicationModule;
import org.glassunit.core.deploy.DirectoryModule;
import org.glassunit.core.deploy.FileModule;
import org.glassunit.core.deploy.JarModule;
import org.glassunit.core.deploy.Module;

/**
 *
 * @author Frederic Müller
 */
public class ModuleUtils {

    /**
     * Creates a new ejb module for the given jar file or directory.
     * @param file jar file or directory
     * @return ejb module
     */
    public static Module createEjbModule(final File file) {
        if (file.isFile()) {
            return new JarModule(file);
        } else {
            return new DirectoryModule(file);
        }
    }

    /**
     * Creates a new ejb module for the given jar file or directory with the given name.
     * @param name module name
     * @param file jar file or directory
     * @return ejb module
     */
    public static Module createEjbModule(final String name, final File file) {
        if (file.isFile()) {
            return new JarModule(name, file);
        } else {
            return new DirectoryModule(name, file);
        }
    }

    /**
     * Creates a new web module for the given jar file or directory.
     * @param file jar file or directory
     * @return web module
     */
    public static Module createWebModule(final File file) {
        if (file.isFile()) {
            return new JarModule(file);
        } else {
            return new DirectoryModule(file);
        }
    }

    /**
     * Creates a new web module with the given webapp directory, classes directory and name.
     * @param name name
     * @param webapp webapp directory
     * @param classes classes directory
     * @return web module
     */
    public static Module createWebModule(final String name, final File webapp, final File classes) {
        final DirectoryModule module = new DirectoryModule(name, webapp);
        module.getSubModules().add(new DirectoryModule("WEB-INF/classes", classes));
        return module;
    }

    /**
     * Creates a new maven artifact module with the given artifact.
     * @param groupId group id
     * @param artifactId artifact id
     * @param version version
     * @param type type
     * @param classifier classifier
     * @param urls repository urls
     * @return maven artifact module
     */
    public static Module createMavenArtifactModule(final String groupId, final String artifactId, final String version, final String type, final String classifier, final String... urls) {
        try {
            final List<ArtifactRepository> repositories = new ArrayList<ArtifactRepository>();
            for (String url : urls) {
                repositories.add(new DefaultArtifactRepository(String.valueOf(System.currentTimeMillis()), url, new DefaultRepositoryLayout()));
            }

            final MavenEmbedder maven = new MavenEmbedder();
            maven.setClassLoader(ModuleUtils.class.getClassLoader());
            maven.start();

            final Artifact artifact = maven.createArtifactWithClassifier(groupId, artifactId, version, type, classifier);
            maven.resolve(artifact, repositories, maven.getLocalRepository());

            return new JarModule(artifact.getFile());
        } catch (final Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Creates a new application module for the current maven project.
     * Source modules as well as dependend web and ejb modules are automatically added.
     * @return application module
     */
    public static ApplicationModule createMavenProjectModule() {
        return createMavenProjectModule(new File("./pom.xml"));
    }

    /**
     * Creates a new application module for the current maven project.
     * Source modules as well as dependend web and ejb modules are automatically added.
     * @return application module
     */
    public static ApplicationModule createMavenProjectModule(final File pomFile) {
        try {
            int moduleCount = 0;
            final MavenEmbedder maven = new MavenEmbedder();
            maven.setClassLoader(ModuleUtils.class.getClassLoader());
            maven.start();

            final MavenProject project = maven.readProject(pomFile);
            final ApplicationModule module = new ApplicationModule(project.getArtifactId());

            final Module libModule = new Module("lib");

            module.getSubModules().add(libModule);
            Logger.getAnonymousLogger().info("Project: " + project.getBasedir().getAbsolutePath());
            for (Dependency dependency : (List<Dependency>) project.getDependencies()) {
                final Artifact artifact = maven.createArtifactWithClassifier(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), dependency.getType(), dependency.getClassifier());
                maven.resolve(artifact, project.getRemoteArtifactRepositories(), maven.getLocalRepository());
                if ("ejb".equals(dependency.getType())) {
                    moduleCount++;
                    module.addEjbModule(ModuleUtils.createEjbModule(artifact.getFile()));
                } else if ("war".equals(dependency.getType())) {
                    moduleCount++;
                    module.addWebModule(ModuleUtils.createWebModule(artifact.getFile()));
                } else {
                    libModule.getSubModules().add(new FileModule(artifact.getFile()));
                }

            }

            for (String source : Arrays.asList("", "test")) {
                final GenericAnnotationDetector webDetector = new GenericAnnotationDetector(new WebSniffer().getAnnotationTypes());
                final GenericAnnotationDetector ejbDetector = new GenericAnnotationDetector(new EjbSniffer().getAnnotationTypes());

                final File webappFolder = new File(project.getBasedir(), "src/" + (source.length() > 0 ? source : "main") + "/webapp");
                final File classesFolder = new File(project.getBasedir(), "target/" + source + (source.length() > 0 ? "-" : "") + "classes");



                if (classesFolder.exists()) {
                    final FileArchive archive = new FileArchive();
                    archive.open(classesFolder.toURI());

                    if (new File(webappFolder, "WEB-INF/web.xml").exists() || webDetector.hasAnnotationInArchive(archive)) {
                        moduleCount++;
                        module.addWebModule(ModuleUtils.createWebModule("project", webappFolder, classesFolder));
                    } else if (archive.exists("META-INF/ejb-jar.xml") || ejbDetector.hasAnnotationInArchive(archive)) {
                        moduleCount++;
                        module.addEjbModule(ModuleUtils.createEjbModule(classesFolder));
                    }
                }

            }

            if (moduleCount == 0) {
                throw new RuntimeException("Maven project does not contain any modules");
            }
            return module;
        } catch (final ProjectBuildingException ex) {
            throw new RuntimeException("Could not read project", ex);
        } catch (final MavenEmbedderException ex) {
            throw new RuntimeException("Could not start maven", ex);
        } catch (final ArtifactNotFoundException ex) {
            throw new RuntimeException("Could not find artifact", ex);
        } catch (final ArtifactResolutionException ex) {
            throw new RuntimeException("Could not resolve artifact", ex);
        } catch (final IOException ex) {
            throw new RuntimeException("Could not read archive", ex);
        }
    }
}
